﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using EnvDTE;
using EnvDTE80;
using Jwell.Template.App.MyWindow;
using Jwell.Template.App.Utl;
using Microsoft.Build.Evaluation;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell.Interop;
using Project = EnvDTE.Project;
using ProjectItem = EnvDTE.ProjectItem;

namespace Jwell.Template.App
{
    /// <summary>
    /// 处理解决方案的创建工作
    /// </summary>
    public class SolutionCreator
    {
        private readonly IServiceProvider _serverProvider;
        private readonly SolutionOption _option;

        /// <summary>
        /// 获取EnvDte2对象
        /// </summary>
        private readonly DTE2 Dte;

        /// <summary>
        /// 所需的SVsSolution服务
        /// </summary>
        private readonly IVsSolution VsSolution;

        public SolutionCreator(IServiceProvider serverProvider, SolutionOption option)
        {
            this._serverProvider = serverProvider;
            this._option = option;
            this.VsSolution = this._serverProvider.GetService(typeof(SVsSolution)) as IVsSolution;
            this.Dte = this._serverProvider.GetService(typeof(DTE)) as DTE2;
        }

        /// <summary>
        ///仅仅创建一个解决方案
        /// </summary>
        /// <param name="location"></param>
        /// <param name="name"></param>
        public void CreateSolutionOnly(string location, string name)
        {
            if (string.IsNullOrWhiteSpace(location) || string.IsNullOrWhiteSpace(name))
            {
                throw new Exception("路径以及名称不能为空");
            }

            var solution = this.VsSolution;
            var dte = (DTE2)this._serverProvider.GetService(typeof(DTE));
            /*
             * * * * 1.检查当前是否存在一个解决方案,如果有,则关闭
             */
            this.CheckAndCloseSolution(solution);

            /*
             * * * * 2.创建一个新的解决方案,并保存
             */
            solution.CreateSolution(location, name, (uint)__VSCREATESOLUTIONFLAGS.CSF_SILENT);

            solution.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, null, 0); //强制保存


        }

        /// <summary>
        /// 从模板文件中创建
        /// </summary>
        /// <param name="templatePath"></param>
        public void CreateProjecsFromTemplate(string templatePath)
        {
            var preFix = "Jwell.SSO";
            var solution = this.VsSolution;
            var solutionDirectonry = solution.GetSolutionDirectory();
            Guid rguidProjectType = Guid.Empty;
            Guid iidProject = Guid.Empty;
            IntPtr ptr = IntPtr.Zero;

            var files = Directory.GetFiles(templatePath, "*.csproj", SearchOption.AllDirectories).ToList() as IList<string>;
            //要排除package目录
            files = files.Remove(u => u.Contains("packages"));

            foreach (string file in files)
            {
                var fileName = Path.GetFileNameWithoutExtension(file);
                var newFileName = fileName.Replace(preFix, this._option.RootNameSpace);
                var projectDirectory = Path.Combine(solutionDirectonry, fileName).Replace(preFix, _option.RootNameSpace);
                Directory.CreateDirectory(projectDirectory);

                var flags = (uint)(__VSCREATEPROJFLAGS.CPF_CLONEFILE | __VSCREATEPROJFLAGS.CPF_OVERWRITE);
                var result = solution.CreateProject(ref rguidProjectType, file, projectDirectory, newFileName, flags,
                    ref iidProject, out ptr);
            }
            //1.查找sln中引用了不在解决方案目录的项目
            var outProjectsPath = this.LookSlnOutProjects(templatePath);
            var outProjectsnName = new List<string>();

            foreach (string outProjectPath in outProjectsPath)//这里就只是单纯的打开项目
            {
                outProjectsnName.Add(Path.GetFileNameWithoutExtension(outProjectPath));
                var flags = (uint)(__VSCREATEPROJFLAGS.CPF_OPENFILE);
                //这里只是打开项目而已
                var result = solution.CreateProject(ref rguidProjectType, outProjectPath, null, null, flags,
                    ref iidProject, out ptr);
            }


            /*
             * * * * 1.更改引用相关的内容
             */
            foreach (Project project in this.Dte.Solution.Projects)
            {
                /*
                 * 修改csharp项目的顶级命名空间以及程序集的名称
                 */
                project.ChangeProjectProperty("RootNamespace", u => u.Replace(preFix, this._option.RootNameSpace));
                project.ChangeProjectProperty("AssemblyName", u => u.Replace(preFix, this._option.RootNameSpace));

                /*
                 * 递归修改.cs文件的命名空间
                 */
                foreach (ProjectItem projectItem in project.ProjectItems)
                {
                    this.ChangeProjectItemNameSpace(projectItem);
                }
                var buildProject = ProjectCollection.GlobalProjectCollection.GetLoadedProjects(project.FullName)
                    .First();
                //修改项目引用位置
                foreach (var item in buildProject.Items.Where(u => u.ItemType == "ProjectReference"))
                {
                    var s = item.GetMetadata("Name");
                    if (outProjectsnName.Contains(s.EvaluatedValue))
                    {
                        //这里要修改Include对应的Attribute属性值
                        var projectPath = project.FullName;
                        var comonProjectPath = outProjectsPath.FirstOrDefault(u => u.Contains(s.EvaluatedValue));
                        var relative = RelativeHelper.RelativePath(comonProjectPath, projectPath);
                        item.UnevaluatedInclude = relative;
                        continue;
                    }
                    item.SetMetadataValue("Name", s.EvaluatedValue.Replace(preFix, this._option.RootNameSpace));//有效

                }
                project.Save();
            }

        }

        /// <summary>
        /// 递归改变代码文件的guid
        /// </summary>
        /// <param name="projectItem"></param>
        private void ChangeProjectItemNameSpace(ProjectItem projectItem)
        {
            if (projectItem.Kind.Equals("{6bb5f8ef-4483-11D3-8bcf-00c04f8ec28c}",
                StringComparison.CurrentCultureIgnoreCase))//表示这个子项是一个文件夹
            {
                foreach (ProjectItem itemProjectItem in projectItem.ProjectItems)
                {
                    this.ChangeProjectItemNameSpace(itemProjectItem);
                }
            }
            //1.不是的话进行命名空间替换
            if (projectItem.FileCodeModel != null)
            {
                foreach (CodeElement codeElement in projectItem.FileCodeModel.CodeElements)
                {
                    if (codeElement.Kind == vsCMElement.vsCMElementNamespace)
                    {
                        codeElement.Name = codeElement.Name.Replace("Jwell.SSO", this._option.RootNameSpace);
                    }

                    if (codeElement.Kind == vsCMElement.vsCMElementUsingStmt)
                    {
                        var name = codeElement.Name;
                        var fullname = codeElement.FullName;
                    }
                }
            }
        }

        //寻找解决方案之外的项目路径(full Path绝对路径)
        private IList<string> LookSlnOutProjects(string templatePath)
        {
            var list = new List<string>();
            var slnfiles = Directory.GetFiles(templatePath, "*.sln", SearchOption.TopDirectoryOnly);
            if (slnfiles.Length == 0)
                return list;
            var slnPath = slnfiles[0];
            var slnText = File.ReadAllText(slnPath);
            var regex = new Regex(@"""(..\\[\w\W]+?)"", """);
            var matches = regex.Matches(slnText);
            if (matches.Count == 0)
                return list;
            foreach (Match match in matches)
            {
                var item = match.Groups[1].Value;
                list.Add(Path.GetFullPath(Path.Combine(templatePath, item)));
            }
            return list;
        }

        private void CheckAndCloseSolution(IVsSolution solution)
        {
            object isSolutionOpen = null;
            solution.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpen, out isSolutionOpen);
            if (Convert.ToBoolean(isSolutionOpen))//表示当前有解决方案
            {
                solution.CloseSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_PromptSave, null, 0);
                solution.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, null, 0); //强制保存
            }
        }
    }
}
